﻿using Bentley.DgnPlatformNET.Elements;
using Bentley.DgnPlatformNET;
using Bentley.GeometryNET;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static System.Windows.Forms.VisualStyles.VisualStyleElement.ToolTip;
using Bentley.MstnPlatformNET;
using Bentley.CifNET.LinearGeometry;
using CheccFillTheChute.Operation;
using CheccCoverplateCulvertAssemble.Tools;
using CheccCoverplateCulvertAssemble.Common.Total;

namespace CheccFillTheChute.Tools
{
    internal class Rhone
    {
        private readonly RhoneBase rhoneBase;
        //涵洞宽
        private readonly double culvertWidth;
        //洞口夹角
        private readonly double angle;
        public Rhone(RhoneBase rhoneBase, double culvertWidth, double angle) 
        { 
            this.rhoneBase = rhoneBase;
            this.culvertWidth = culvertWidth;   
            this.angle = angle;
        }
        public Element CreateEntityModel()
        {
            var objParameter = GetUorCoverParameter();

            double bottomWidthLeft_R = objParameter. outsideSlope == 0 ? 0 : objParameter.highEndHeight * objParameter.outsideSlope;
            //double rightAngleSide = bottomWidthLeft_R / Math.Cos((90 - angle) / 180 * Math.PI);
           // rightAngleSide += objParameter.topWidth / Math.Cos((90 - angle) / 180 * Math.PI);
            double bottomWidthRight_R = objParameter.insideSlope == 0 ? 0 : objParameter.highEndHeight * objParameter.insideSlope;
            //rightAngleSide += bottomWidthRight_R / Math.Cos((90 - angle) / 180 * Math.PI);

            double rightAngleSide = bottomWidthLeft_R + bottomWidthRight_R + objParameter.topWidth;
            //槽底
            List<DPoint3d> tankBottomTopDpoint3ds = GetTankBottomPoints(objParameter.tankBottomHeight, objParameter.unilateralWidth * 2 + culvertWidth+2* rightAngleSide, objParameter.rhoneLength, objParameter.openingSlope, angle);
            var CulvertRhoneBody_R = CreateRightCulvertRhoneBody(tankBottomTopDpoint3ds, objParameter.highEndHeight, objParameter.lowSideHeight, objParameter.topWidth, objParameter.insideSlope, objParameter.outsideSlope, angle);
            var CulvertRhoneBody_L = CreateLeftCulvertRhoneBody(tankBottomTopDpoint3ds, objParameter.highEndHeight, objParameter.lowSideHeight, objParameter.topWidth, objParameter.insideSlope, objParameter.outsideSlope, angle);

            SolidKernelEntity CulvertRhoneBodyElement_L = CulvertRhoneBody_L.Item1;
            SolidKernelEntity CulvertRhoneBodyElement_R = CulvertRhoneBody_R.Item1;

            tankBottomTopDpoint3ds[0] = DPoint3d.FromXYZ(tankBottomTopDpoint3ds[0].X, tankBottomTopDpoint3ds[0].Y, tankBottomTopDpoint3ds[0].Z - objParameter.tankBottomHeight);
            tankBottomTopDpoint3ds[1] = DPoint3d.FromXYZ(tankBottomTopDpoint3ds[1].X, tankBottomTopDpoint3ds[1].Y, tankBottomTopDpoint3ds[1].Z- objParameter.tankBottomHeight);

            tankBottomTopDpoint3ds[2] = DPoint3d.FromXYZ(CulvertRhoneBody_L.Item2.X, CulvertRhoneBody_L.Item2.Y, CulvertRhoneBody_L.Item2.Z - objParameter.tankBottomHeight); 
            tankBottomTopDpoint3ds[3] = DPoint3d.FromXYZ(CulvertRhoneBody_R.Item2.X, CulvertRhoneBody_R.Item2.Y, CulvertRhoneBody_R.Item2.Z - objParameter.tankBottomHeight);
            tankBottomTopDpoint3ds = ConvertListUORPoint(tankBottomTopDpoint3ds);
            SolidKernelEntity TankBottomElement = CreatePointSmartSolid(tankBottomTopDpoint3ds, objParameter.tankBottomHeight);

            List<SolidKernelEntity> solidKernelEntities = new();
            solidKernelEntities.Add(CulvertRhoneBodyElement_R);
            solidKernelEntities.Add(CulvertRhoneBodyElement_L);        
            SolidKernelEntity[] tools = solidKernelEntities.ToArray();
            SolidKernelEntity target = TankBottomElement;
            Bentley.CIMNET.CIMGeometry.Solid.SolidUtil.Modify.BodyBooleanUnion(ref target, ref tools, 2);
            Convert1.BodyToElement(out Element resultSolid, target, null, GeneralBank.ActiveDgnModel);

            //位移
            DPoint3d movePt = new DPoint3d(0, 0, -objParameter.tankBottomHeight) * GeneralBank.UorToMeters; ;
            DTransform3d trans = new DTransform3d(DMatrix3d.Identity);
            trans.Translation = movePt;
            resultSolid.ApplyTransform(new TransformInfo(trans));
            return resultSolid;
        }

        private (double highEndHeight, double lowSideHeight, double rhoneLength, double openingSlope, double topWidth, double unilateralWidth, double insideSlope, double outsideSlope, double tankBottomHeight) GetUorCoverParameter()
        {
            double highEndHeight = this.rhoneBase.TopHeight /100;
            double lowSideHeight = this.rhoneBase.LowHeight /100;
            double rhoneLength = this.rhoneBase.Length / 100;
            double openingSlope = this.rhoneBase.LongitudinalSlopeOfEntrance;
            double topWidth = this.rhoneBase.TopWidth / 100;
            double unilateralWidth = this.rhoneBase.WidenOnOneSide / 100;
            double insideSlope = this.rhoneBase.InsideGrade / 100;
            double outsideSlope = this.rhoneBase.OutsideGrade;
            double tankBottomHeight = this.rhoneBase.GrooveBottomThickness / 100;
            return (highEndHeight, lowSideHeight, rhoneLength, openingSlope, topWidth, unilateralWidth, insideSlope, outsideSlope,tankBottomHeight);
        }

        private (SolidKernelEntity, DPoint3d) CreateRightCulvertRhoneBody(List<DPoint3d> tankBottomTopDpoint3ds, double highEndHeight, double lowSideHeight, double topWidth, double insideSlope, double outsideSlope,double angle)
        {
            LinearElement linearElement_R = Line.Create1(tankBottomTopDpoint3ds[2], tankBottomTopDpoint3ds[3]);

            double bottomWidthLeft_R = outsideSlope == 0 ? 0 : highEndHeight * outsideSlope;
           
            #region 靠近洞口的点
            double rightAngleSide =bottomWidthLeft_R / Math.Cos((90-angle) / 180 * Math.PI);
            //右上
            DPoint3d topRight_R = GetNformMNAndLL(tankBottomTopDpoint3ds[0], tankBottomTopDpoint3ds[1], rightAngleSide);
            //左上
            rightAngleSide =topWidth / Math.Cos((90-angle) / 180 * Math.PI);      
            DPoint3d topLeft_R = GetNformMNAndLL(topRight_R, tankBottomTopDpoint3ds[1], rightAngleSide);
            //左下
            double bottomWidthRight_R = insideSlope == 0 ? 0 : highEndHeight * insideSlope;
            rightAngleSide = bottomWidthRight_R / Math.Cos((90 - angle) / 180 * Math.PI);
            DPoint3d bottomLeft_R = GetNformMNAndLL(topLeft_R, tankBottomTopDpoint3ds[1],rightAngleSide);
            topRight_R.Z = tankBottomTopDpoint3ds[0].Z + highEndHeight;
            topLeft_R.Z = tankBottomTopDpoint3ds[0].Z + highEndHeight;
            //右下
            DPoint3d bottomRight_R = tankBottomTopDpoint3ds[0];
            #endregion

            #region 外侧点
            LinearElement TopRightLinearElement = Line.Create1(topRight_R, GetNformMNAndLL(topRight_R, DPoint3d.FromXYZ(topRight_R.X + 1, topRight_R.Y, topRight_R.Z), 1000));
            DPoint3d insertDpoint = linearElement_R.Intersect(TopRightLinearElement).Item(0).PointOn1.Coordinates;
            //右上
            DPoint3d topRight_R2 = DPoint3d.FromXYZ(insertDpoint.X, insertDpoint.Y, insertDpoint.Z + lowSideHeight);
            //左上
            LinearElement TopLeftLinearElement = Line.Create1(topLeft_R, GetNformMNAndLL(topLeft_R, DPoint3d.FromXYZ(topLeft_R.X + 1, topLeft_R.Y, topLeft_R.Z), 1000));
            insertDpoint = linearElement_R.Intersect(TopLeftLinearElement).Item(0).PointOn1.Coordinates;
            DPoint3d topLeft_R2 = DPoint3d.FromXYZ(insertDpoint.X, insertDpoint.Y, insertDpoint.Z + lowSideHeight);
            //左下
            bottomWidthRight_R = insideSlope == 0 ? 0 : lowSideHeight * insideSlope;
            rightAngleSide =bottomWidthRight_R / Math.Cos((90 - angle) / 180 * Math.PI);
            DPoint3d bottomLeft_R2 = GetNformMNAndLL(insertDpoint, tankBottomTopDpoint3ds[2], rightAngleSide);
            //右下
            bottomWidthLeft_R = outsideSlope == 0 ? 0 : lowSideHeight * outsideSlope;
            rightAngleSide = bottomWidthLeft_R / Math.Cos((90 - angle) / 180 * Math.PI);
            DPoint3d bottomRight_R2 = GetNformMNAndLL(DPoint3d.FromXYZ(topRight_R2.X, topRight_R2.Y, topRight_R2.Z- lowSideHeight) , tankBottomTopDpoint3ds[3], rightAngleSide);
            #endregion
            List<CurveVector> guidesIn = new()
            {
               new ShapeElement(GeneralBank.ActiveDgnModel,null,ConvertListUORPoint(new List<DPoint3d>(){ topRight_R,topLeft_R,bottomLeft_R,bottomRight_R }).ToArray() ).GetCurveVector(),
               new ShapeElement(GeneralBank.ActiveDgnModel,null,ConvertListUORPoint( new List<DPoint3d>(){ topRight_R2,topLeft_R2,bottomLeft_R2,bottomRight_R2 }).ToArray() ).GetCurveVector()
            };
            List<CurveVector> profilesIn = new()
            {
               new LineElement(GeneralBank.ActiveDgnModel,null,new DSegment3d(GetUORPoint(topRight_R),GetUORPoint(topRight_R2))).GetCurveVector(),
               new LineElement(GeneralBank.ActiveDgnModel,null,new DSegment3d(GetUORPoint(topLeft_R),GetUORPoint(topLeft_R2))).GetCurveVector(),
               new LineElement(GeneralBank.ActiveDgnModel,null,new DSegment3d(GetUORPoint(bottomLeft_R),GetUORPoint(bottomLeft_R2))).GetCurveVector(),
               new LineElement(GeneralBank.ActiveDgnModel,null,new DSegment3d(GetUORPoint(bottomRight_R),GetUORPoint(bottomRight_R2))).GetCurveVector()
            };

            Create.BodyFromLoft(out SolidKernelEntity entityOut, guidesIn.ToArray(), 2, profilesIn.ToArray(), 4, GeneralBank.ActiveDgnModel, false, false);
            if (entityOut != null)
            {
                BentleyStatus success1 = Convert1.BodyToElement(out Element ehOut, entityOut, null, GeneralBank.ActiveDgnModel);
                if (success1 == BentleyStatus.Success) return (entityOut, bottomRight_R2);
            }
            return (null,DPoint3d.Zero);
        }

        private (SolidKernelEntity, DPoint3d) CreateLeftCulvertRhoneBody(List<DPoint3d> tankBottomTopDpoint3ds, double highEndHeight, double lowSideHeight, double topWidth, double insideSlope, double outsideSlope,double angle)
        {
            LinearElement linearElement_R = Line.Create1(tankBottomTopDpoint3ds[2], tankBottomTopDpoint3ds[3]);

            double bottomWidthLeft_R = outsideSlope == 0 ? 0 : highEndHeight * outsideSlope;

            #region 靠近洞口的点
            double rightAngleSide = bottomWidthLeft_R / Math.Cos((90 - angle) / 180 * Math.PI);
            //右上
            DPoint3d topRight_R = GetNformMNAndLL(tankBottomTopDpoint3ds[1], tankBottomTopDpoint3ds[0], rightAngleSide);
            //左上
            rightAngleSide = topWidth / Math.Cos((90 - angle) / 180 * Math.PI);
            DPoint3d topLeft_R = GetNformMNAndLL(topRight_R, tankBottomTopDpoint3ds[0], rightAngleSide);
            //左下
            double bottomWidthRight_R = insideSlope == 0 ? 0 : highEndHeight * insideSlope;
            rightAngleSide = bottomWidthRight_R / Math.Cos((90 - angle) / 180 * Math.PI);
            DPoint3d bottomLeft_R = GetNformMNAndLL(topLeft_R, tankBottomTopDpoint3ds[0], rightAngleSide);
            topRight_R.Z = tankBottomTopDpoint3ds[1].Z + highEndHeight;
            topLeft_R.Z = tankBottomTopDpoint3ds[1].Z + highEndHeight;
            //右下
            DPoint3d bottomRight_R = tankBottomTopDpoint3ds[1];
            #endregion

            #region 外侧点
            LinearElement TopRightLinearElement = Line.Create1(topRight_R, GetNformMNAndLL(topRight_R, DPoint3d.FromXYZ(topRight_R.X + 1, topRight_R.Y, topRight_R.Z), 1000));
            DPoint3d insertDpoint = linearElement_R.Intersect(TopRightLinearElement).Item(0).PointOn1.Coordinates;
            //右上
            DPoint3d topRight_R2 = DPoint3d.FromXYZ(insertDpoint.X, insertDpoint.Y, insertDpoint.Z + lowSideHeight);
            //左上
            LinearElement TopLeftLinearElement = Line.Create1(topLeft_R, GetNformMNAndLL(topLeft_R, DPoint3d.FromXYZ(topLeft_R.X + 1, topLeft_R.Y, topLeft_R.Z), 1000));
            insertDpoint = linearElement_R.Intersect(TopLeftLinearElement).Item(0).PointOn1.Coordinates;
            DPoint3d topLeft_R2 = DPoint3d.FromXYZ(insertDpoint.X, insertDpoint.Y, insertDpoint.Z + lowSideHeight);
            //左下
            bottomWidthRight_R = insideSlope == 0 ? 0 : lowSideHeight * insideSlope;
            rightAngleSide = bottomWidthRight_R / Math.Cos((90 - angle) / 180 * Math.PI);
            DPoint3d bottomLeft_R2 = GetNformMNAndLL(insertDpoint, tankBottomTopDpoint3ds[3], rightAngleSide);
            //右下
            bottomWidthLeft_R = outsideSlope == 0 ? 0 : lowSideHeight * outsideSlope;
            rightAngleSide = bottomWidthLeft_R / Math.Cos((90 - angle) / 180 * Math.PI);
            DPoint3d bottomRight_R2 = GetNformMNAndLL(DPoint3d.FromXYZ(topRight_R2.X, topRight_R2.Y, topRight_R2.Z - lowSideHeight), tankBottomTopDpoint3ds[2], rightAngleSide);
            #endregion

            List<CurveVector> guidesIn = new()
            {
               new ShapeElement(GeneralBank.ActiveDgnModel,null,ConvertListUORPoint(new List<DPoint3d>(){ topRight_R,topLeft_R,bottomLeft_R,bottomRight_R }).ToArray() ).GetCurveVector(),
               new ShapeElement(GeneralBank.ActiveDgnModel,null,ConvertListUORPoint( new List<DPoint3d>(){ topRight_R2,topLeft_R2,bottomLeft_R2,bottomRight_R2 }).ToArray() ).GetCurveVector()
            };
            List<CurveVector> profilesIn = new()
            {
               new LineElement(GeneralBank.ActiveDgnModel,null,new DSegment3d(GetUORPoint(topRight_R),GetUORPoint(topRight_R2))).GetCurveVector(),
               new LineElement(GeneralBank.ActiveDgnModel,null,new DSegment3d(GetUORPoint(topLeft_R),GetUORPoint(topLeft_R2))).GetCurveVector(),
               new LineElement(GeneralBank.ActiveDgnModel,null,new DSegment3d(GetUORPoint(bottomLeft_R),GetUORPoint(bottomLeft_R2))).GetCurveVector(),
               new LineElement(GeneralBank.ActiveDgnModel,null,new DSegment3d(GetUORPoint(bottomRight_R),GetUORPoint(bottomRight_R2))).GetCurveVector()
            };

            Create.BodyFromLoft(out SolidKernelEntity entityOut, guidesIn.ToArray(), 2, profilesIn.ToArray(), 4, GeneralBank.ActiveDgnModel, false, false);
            if (entityOut != null)
            {
                BentleyStatus success1 = Convert1.BodyToElement(out Element ehOut, entityOut, null, GeneralBank.ActiveDgnModel);
                if (success1 == BentleyStatus.Success) return (entityOut, bottomRight_R2);
            }
            return (null,DPoint3d.Zero);
        }

        /// <summary>
        /// 槽底边坡点
        /// </summary>
        /// <param name="height">厚度</param>
        /// <param name="width">宽度</param>
        /// <param name="length">长度</param>
        /// <param name="slope">纵坡</param>
        private List<DPoint3d>  GetTankBottomPoints(double height,double width,double length,double slope,double angle)
        {
            double LeftLateralOffset = Math.Abs((width/2 )* Math.Tan((90 - angle) * Math.PI / 180));

            DPoint3d bottomInside_R = DPoint3d.FromXYZ(0, -width / 2, 0);
            DPoint3d bottomInside_L = DPoint3d.FromXYZ(0, width / 2, 0);
            if (angle <= 90)
            {
                bottomInside_R = DPoint3d.FromXYZ(-LeftLateralOffset, -width / 2, 0);
                bottomInside_L = DPoint3d.FromXYZ(LeftLateralOffset, width / 2, 0);
                
            }
            else if (angle > 90)
            {
                bottomInside_R = DPoint3d.FromXYZ(LeftLateralOffset, -width / 2, 0);
                bottomInside_L = DPoint3d.FromXYZ(-LeftLateralOffset, width / 2, 0);
            }
            double b = bottomInside_R.Y - bottomInside_R.X * slope;
            double nextY = (bottomInside_R.X+1) * slope + b;
            DPoint3d nextDpoint3d_L = DPoint3d.FromXYZ(bottomInside_R.X + 1, -width / 2, nextY- bottomInside_R.Y);
            DPoint3d nextDpoint3d_R = DPoint3d.FromXYZ(bottomInside_L.X + 1, width / 2, nextY - bottomInside_R.Y);
            double newLength= Math.Sqrt( Math.Pow(length,2) + Math.Pow(length * slope, 2));

            DPoint3d bottomOutside_L = GetNformMNAndLL(bottomInside_R, nextDpoint3d_L, newLength);
            DPoint3d bottomOutside_R = GetNformMNAndLL(bottomInside_L, nextDpoint3d_R, newLength);
            List<DPoint3d> tankBottomTopDpoint3ds = new ()
            {
                DPoint3d.FromXYZ(bottomInside_R.X, bottomInside_R.Y, bottomInside_R.Z+ height),
                DPoint3d.FromXYZ(bottomInside_L.X, bottomInside_L.Y, bottomInside_L.Z+ height) ,
                DPoint3d.FromXYZ(bottomOutside_R.X,bottomOutside_R.Y,bottomOutside_R.Z+height) ,
                DPoint3d.FromXYZ(bottomOutside_L.X,bottomOutside_L.Y,bottomOutside_L.Z+height)
            };

            //LineStringElement lineStringElement = new LineStringElement(Session.Instance.GetActiveDgnModel(),null, ConvertListUORPoint(tankBottomTopDpoint3ds).ToArray());
            //lineStringElement.AddToModel();

            return tankBottomTopDpoint3ds;

        }

        /// <summary>
        /// 创建实体 点集有分辨率
        /// </summary>
        /// <param name="points"></param>
        /// <param name="Width"></param>
        private SolidKernelEntity CreatePointSmartSolid(List<DPoint3d> points, double Width)
        {
            ShapeElement sectiongElement = new ShapeElement(GeneralBank.ActiveDgnModel, null, points.ToArray());
            CurveVector secCur = CurvePathQuery.ElementToCurveVector(sectiongElement);
            SolidKernelEntity solidKernel = CreateBodyFromExtrusion(secCur, Width, DPoint3d.Zero, DVector3d.UnitZ, GeneralBank.ActiveDgnModel);
            Convert1.BodyToElement(out Element ehOut, solidKernel, null, GeneralBank.ActiveDgnModel);
            return solidKernel;
        }

        private SolidKernelEntity CreateBodyFromExtrusion(CurveVector secCur, double extrusionLength, DPoint3d centroid, DVector3d extrudDir, DgnModel modelRef)
        {
            BentleyStatus status = Bentley.CIMNET.CIMGeometry.Solid.SolidUtil.Create.BodyFromExtrusion(
                out SolidKernelEntity entity, secCur, extrusionLength * modelRef.GetModelInfo().UorPerMaster, modelRef, extrudDir, false, null);
            return entity;
        }


        /// <summary>
        /// 计算延长点
        /// </summary>
        /// <param name="M">原点</param>
        /// <param name="N">指向点</param>
        /// <param name="LL">指定延长的长度</param>
        /// <returns></returns>
        private  DPoint3d GetNformMNAndLL(DPoint3d M, DPoint3d N, double LL)
        {
            DPoint3d PT = new DPoint3d();

            DVector3d VM = new DVector3d(M);
            DVector3d VN = new DVector3d(N);

            DVector3d MN = new DVector3d();

            MN = DVector3d.Subtract(VN, VM);

            double len = VM.Distance(VN);
            double Bei = (LL / len);

            MN.ScaleInPlace(Bei);
            VN = DVector3d.Add(MN, VM);
            PT = new DPoint3d(VN);

            return PT;
        }

        private DPoint3d GetMeterPoint(DPoint3d uorpoint)
        {
            double u = UorPerMeter();
            return DPoint3d.FromXYZ(
                uorpoint.X / u,
                uorpoint.Y / u,
                uorpoint.Z / u);
        }

        private DPoint3d GetUORPoint(DPoint3d meterpoint)
        {
            double u = UorPerMeter();

            return DPoint3d.FromXYZ(
                meterpoint.X * u,
                meterpoint.Y * u,
                meterpoint.Z * u);
        }

        private List<DPoint3d> ConvertListMeterPoint(List<DPoint3d> dPoints)
        {
            List<DPoint3d> points = new List<DPoint3d>();
            for (int i = 0; i < dPoints.Count; i++)
            {
                points.Add(GetMeterPoint(dPoints[i]));
            }
            return points;
        }

        private List<DPoint3d> ConvertListUORPoint(List<DPoint3d> dPoints)
        {
            List<DPoint3d> points = new List<DPoint3d>();
            for (int i = 0; i < dPoints.Count; i++)
            {
                points.Add(GetUORPoint(dPoints[i]));
            }
            return points;
        }

        private double UorPerMeter()
        {
            ModelInfo modelInfo = Bentley.MstnPlatformNET.Session.Instance.GetActiveDgnModel().GetModelInfo();
            double uorsPerMeter = modelInfo.UorPerMeter;
            return uorsPerMeter;
        }


    }
}
